import React, { useCallback, useEffect, useRef, useState } from 'react';
import './Carousel.less';

interface SlideProps {
  content: React.ReactNode;
}

interface CarouselIndicatorsProps {
  position: string;
  activeIndex: number;
  indicatorsColor: string;
  clickHandler: (index: number) => void;
  slides: SlideProps[];
}

interface CarouselProps {
  autoPlay?: boolean;
  activeSlideDuration?: number;
  interactionMode?: 'swipe' | 'hover';
  indicatorsColor?: string;
  alignIndicators?: 'center' | 'left' | 'right';
  alignCaption?: 'center' | 'left' | 'right';
  slides: SlideProps[];
}

const Slide: React.FC<SlideProps> = React.memo(({ content }) => (
  <div className="carousel-slide-content">{content}</div>
));

const CarouselIndicators: React.FC<CarouselIndicatorsProps> = React.memo(
  ({ position, activeIndex, indicatorsColor, clickHandler, slides }) => (
    <ol className={`carousel-indicators ${position}`}>
      {slides.map((_, i) => (
        <li
          key={i}
          className={i === activeIndex ? 'active' : ''}
          style={{ '--indicatorsColor': indicatorsColor } as React.CSSProperties}
          onClick={() => clickHandler(i)}
        />
      ))}
    </ol>
  ),
);

const Carousel: React.FC<CarouselProps> = ({
  autoPlay = false,
  activeSlideDuration = 3000,
  interactionMode = 'swipe',
  indicatorsColor = '#E0E0E0',
  alignIndicators = 'center',
  alignCaption = 'center',
  slides = [],
}) => {
  const [activeIndex, setActiveIndex] = useState(0);
  const [isAnimating, setIsAnimating] = useState(false);
  const [indicatorPosition, setIndicatorPosition] = useState('position-center');
  const autoSlideRef = useRef<NodeJS.Timeout | null>(null);
  const directionRef = useRef<'to-left' | 'to-right'>('to-left');
  const touchStartXRef = useRef<number | null>(null);

  useEffect(() => {
    setIndicatorPosition(`position-${alignIndicators}`);
  }, [alignIndicators]);

  const stopAutoSliding = useCallback(() => {
    if (autoSlideRef.current) {
      clearInterval(autoSlideRef.current);
      autoSlideRef.current = null;
    }
  }, []);

  const startAutoSliding = useCallback(() => {
    if (autoPlay && !autoSlideRef.current) {
      autoSlideRef.current = setInterval(() => {
        setActiveIndex((prevIndex) => (prevIndex + 1) % slides.length);
      }, activeSlideDuration);
    }
  }, [autoPlay, activeSlideDuration, slides.length]);

  useEffect(() => {
    startAutoSliding();
    return stopAutoSliding;
  }, [startAutoSliding, stopAutoSliding]);

  const handleSlideChange = useCallback(
    (newIndex: number) => {
      if (isAnimating) return;
      setIsAnimating(true);
      directionRef.current = newIndex > activeIndex ? 'to-left' : 'to-right';
      setActiveIndex(newIndex);
      setTimeout(() => setIsAnimating(false), 600);
    },
    [activeIndex, isAnimating],
  );

  const handleIndicatorClick = useCallback(
    (index: number) => {
      if (index !== activeIndex) {
        stopAutoSliding();
        handleSlideChange(index);
        startAutoSliding();
      }
    },
    [activeIndex, handleSlideChange, startAutoSliding, stopAutoSliding],
  );

  const handleTouchStart = useCallback(
    (e: React.TouchEvent) => {
      if (interactionMode === 'swipe') {
        touchStartXRef.current = e.touches[0].clientX;
      }
    },
    [interactionMode],
  );

  const handleTouchEnd = useCallback(
    (e: React.TouchEvent) => {
      if (interactionMode === 'swipe' && touchStartXRef.current !== null) {
        const touchEndX = e.changedTouches[0].clientX;
        const diff = touchStartXRef.current - touchEndX;
        if (Math.abs(diff) > 50) {
          const newIndex =
            diff > 0
              ? (activeIndex + 1) % slides.length
              : (activeIndex - 1 + slides.length) % slides.length;
          handleSlideChange(newIndex);
        }
        touchStartXRef.current = null;
      }
    },
    [interactionMode, activeIndex, slides.length, handleSlideChange],
  );

  return (
    <div
      className="carousel-slider-wrapper"
      style={{ cursor: interactionMode === 'swipe' ? 'grab' : 'default' }}
      onTouchStart={handleTouchStart}
      onTouchEnd={handleTouchEnd}
      onMouseEnter={interactionMode === 'hover' ? stopAutoSliding : undefined}
      onMouseLeave={interactionMode === 'hover' ? startAutoSliding : undefined}
    >
      {slides.map((slide, index) => (
        <div
          key={index}
          className={`carousel-slide ${index === activeIndex ? 'active-slide' : ''} ${
            isAnimating
              ? `${
                  directionRef.current === 'to-left'
                    ? 'slide-enter-from-right'
                    : 'slide-enter-from-left'
                }`
              : ''
          } ${
            index === (activeIndex - 1 + slides.length) % slides.length ? 'previous-slide' : ''
          } ${index === (activeIndex + 1) % slides.length ? 'next-slide' : ''}`}
          style={{ textAlign: alignCaption }}
        >
          <Slide content={slide.content} />
        </div>
      ))}
      <CarouselIndicators
        position={indicatorPosition}
        activeIndex={activeIndex}
        indicatorsColor={indicatorsColor}
        clickHandler={handleIndicatorClick}
        slides={slides}
      />
    </div>
  );
};

export default Carousel;
